/*
 ******************************************************************************
 *  Java Signal Framework for routing java objects. 
 *  Copyright (C) 2008  Manuel Stahl (thymythos@web.de)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ****************************************************************************** 
 */
package org.tru42.signal.model;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map.Entry;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.tru42.signal.lang.ConnectException;
import org.tru42.signal.lang.ISignal;


public class SignalConnectionXMLReader {

	protected InputStream inStream;
	
	public SignalConnectionXMLReader(InputStream inStream) {
		this.inStream = inStream;
	}
	
	public SignalDiagram readModel() throws IOException {
		SignalDiagram model = new SignalDiagram();
		XMLInputFactory inputFactory = XMLInputFactory.newInstance();
		try {
			XMLStreamReader reader = inputFactory.createXMLStreamReader(inStream);
			
			if(reader.nextTag() != XMLStreamReader.START_ELEMENT || !reader.getLocalName().equals("SignalConnection"))
				return model;
			
			model.setName(reader.getAttributeValue("", "name"));
			
			if(reader.nextTag() == XMLStreamReader.START_ELEMENT) {
			
				if(reader.getLocalName().equals("Processors"))
					readProcessors(reader, model);
					
				if(reader.getLocalName().equals("Connections"))
					readConnections(reader, model);
			}
			
		} catch (XMLStreamException e) {
			throw new IOException("XML error");
		}
		
		return model;
	}

	protected ISignalProcessor createProcessor(String id) throws Exception {
		Class<?> c = Class.forName(id);
		Object obj = c.newInstance();
		return (ISignalProcessor) obj;
	}
	
	protected void processProcessorAttribute(ISignalProcessor proc, String key, String value) {
		if(key.equals("name"))
			proc.setName(value);
	}
	
	private void readProcessors(XMLStreamReader reader, SignalDiagram model) throws XMLStreamException {
		while(reader.hasNext() && !(reader.next() == XMLStreamReader.END_ELEMENT && reader.getLocalName().equals("Processors")) ) {
			if(reader.getEventType() == XMLStreamReader.START_ELEMENT && reader.getLocalName().equals("SignalProcessor")) {
				HashMap<String, String> attr = getAttributes(reader);
				try {
					ISignalProcessor proc = createProcessor(attr.get("class"));
					for(Entry<String,String> e:attr.entrySet())
						processProcessorAttribute(proc, e.getKey(), e.getValue());
					model.addSignalProcessor(proc);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		reader.nextTag();
	}
	
	private void readConnections(XMLStreamReader reader, SignalDiagram model) throws XMLStreamException {
		while(reader.hasNext() && !(reader.next() == XMLStreamReader.END_ELEMENT && reader.getLocalName().equals("Connections")) )
			if(reader.getEventType() == XMLStreamReader.START_ELEMENT && reader.getLocalName().equals("Source")) {
				HashMap<String, String> attr = getAttributes(reader);
				ISignalProcessor sourceProc = model.getSignalProcessorByName(attr.get("processor"));

				if(attr.get("name") != null && sourceProc != null) {
					ISignal signal = sourceProc.getSignal(attr.get("name"));
					while(reader.hasNext() && !(reader.next() == XMLStreamReader.END_ELEMENT && reader.getLocalName().equals("Source")) ) {
						if(reader.getEventType() == XMLStreamReader.START_ELEMENT && reader.getLocalName().equals("Sink")) {
							attr = getAttributes(reader);
							ISignalProcessor SinkProc = model.getSignalProcessorByName(attr.get("processor"));
							if(attr.get("name") != null && SinkProc != null) {
								Sink sink = SinkProc.getSink(attr.get("name"));
								try {
									model.connect(signal, sink);
								} catch (ConnectException e) {
									e.printStackTrace();
								}
							}
						}
					}
				}
			}
		reader.nextTag();
	}

	protected HashMap<String, String> getAttributes(XMLStreamReader reader) {
		HashMap<String, String> attributes = new HashMap<String, String>();
		for(int i=0; i<reader.getAttributeCount(); i++)
			attributes.put(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
		return attributes;
	}
	
}
